#include <gtest/gtest.h>
#include <iostream>
#include "csv_row_test.hpp"
#include "council_member_test.hpp"
#include "council_member_manager_test.hpp"
#include "committee_manager_test.hpp"
#include "membership_history_manager_test.hpp"
#include "chairmanship_history_manager_test.hpp"
#include "session_manager_test.hpp"
#include "session_presence_manager_test.hpp"
#include "business_logic_test.hpp"

bool requestContinuation();
bool getCommand(std::string& command);
bool processCommand(const std::string& command);

const std::string c_serializationPath = "../../data";

int main(int argc, char **argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  const int result = RUN_ALL_TESTS();
  std::cout << std::endl;

  if (!requestContinuation())
  {
    std::cout << "Press enter to dismiss this window...";
    std::cin.get();
    return result;
  }

  ObjectModel::instance().deserialize(c_serializationPath);

  std::string command;
  while (getCommand(command))
  {
    if (!processCommand(command))
      std::cout << "Command unrecognized." << std::endl;
  }

  std::cout << "Press enter to dismiss this window...";
  std::cin.get();
  return result;
}

std::string getInputLine(std::istream &stream)
{
  const int c_inputSize = 255;
  char input[c_inputSize];
  memset(&input, 0, sizeof(input));
  std::cin.getline(input, c_inputSize);
  return std::string(input);
}

bool requestContinuation()
{
  std::cout << "Tests performed. If you want to continue to interactive mode, please press 'y'\n>>> ";
  std::string response = getInputLine(std::cin);
  return response == "y";
}

bool getCommand(std::string &command)
{
  std::cout << ">>> ";
  command = getInputLine(std::cin);
  return command != "exit";
}

bool listCommittees()
{
  BOOST_FOREACH(const CommitteeManager::CommitteePair &p, ObjectModel::instance().committees().committees())
  {
    std::cout << p.second.aim() << std::endl;
    std::cout << "Member List: " << std::endl;
    BusinessLogicController controller(ObjectModel::instance());
    std::vector<CouncilMember> members = controller.currentCommitteeMembers(p.second);
    BOOST_FOREACH (const CouncilMember& member, members)
      std::cout << member.name() << std::endl;
    std::cout << "Chairman: " << std::endl;
    CouncilMember chairman = controller.getCurrentCommetteeChairmanship(p.second);
    std::cout << chairman.name() << std::endl << std::endl;
  }
  return true;
}

bool addCommitteeMember()
{
  std::cout << "Committees:" << std::endl;
  BOOST_FOREACH(const CommitteeManager::CommitteePair &p, ObjectModel::instance().committees().committees())
    std::cout << "ID:" << p.second.id() << " Aim: " << p.second.aim() << std::endl;

  std::cout << "Council Members:" << std::endl;
  BOOST_FOREACH(const CouncilMemberManager::CouncilMemberPair &p, ObjectModel::instance().councilMembers().members())
    std::cout << "ID:" << p.second.id() << " Name: " << p.second.name() << std::endl;
  
  std::cout << "Enter id of committee to add member of: ";
  std::string committeId = getInputLine(std::cin);
  
  std::cout << "Enter id of the member to add: ";
  std::string memberId = getInputLine(std::cin);

  MembershipHistory history(ObjectModel::instance().membershipHistory().newHistoryId(),
    memberId, committeId, time(0), c_nullDate);
  ObjectModel::instance().membershipHistory().insert(history);
  ObjectModel::instance().serialize(c_serializationPath);
  return true;
}

bool listMembers()
{
  BusinessLogicController controller(ObjectModel::instance());

  BOOST_FOREACH (const CouncilMemberManager::CouncilMemberPair& p, ObjectModel::instance().councilMembers().members())
  {
    std::cout << "Member: " << p.second.name() << std::endl;
    std::vector<Committee> committees = controller.committeesByMember(p.second);
    BOOST_FOREACH (const Committee& c, committees)
      std::cout << "  " << c.aim() << std::endl;
    std::cout << std::endl;
  }
  return true;
}

bool addCommittee()
{
  std::cout << "Council Members:" << std::endl;
  BOOST_FOREACH(const CouncilMemberManager::CouncilMemberPair &p, ObjectModel::instance().councilMembers().members())
    std::cout << "ID:" << p.second.id() << " Name: " << p.second.name() << std::endl;
  
  std::cout << "Enter aim for the new committee: ";
  std::string aim = getInputLine(std::cin);

  std::cout << "Enter chairman id for the new committee: ";
  std::string chairmanId = getInputLine(std::cin);

  BusinessLogicController controller(ObjectModel::instance());
  controller.addCommittee(aim, chairmanId);
  return true;
}

bool showAbsenceList()
{
  std::cout << "Committees:" << std::endl;
  BOOST_FOREACH(const CommitteeManager::CommitteePair &p, ObjectModel::instance().committees().committees())
    std::cout << "ID:" << p.second.id() << " Aim: " << p.second.aim() << std::endl;

  std::cout << "Enter committee id: ";
  const std::string committeeId = getInputLine(std::cin);
  
  std::cout << "Enter start date (YYYY MM DD): ";
  const std::string startDate = getInputLine(std::cin);
  time_t start = dateFromFormattedString(startDate);

  std::cout << "Enter end date (YYYY MM DD): ";
  const std::string endDate = getInputLine(std::cin);
  time_t end = dateFromFormattedString(endDate);

  BusinessLogicController controller(ObjectModel::instance());
  std::cout << controller.listOfAbsence(*ObjectModel::instance().committees().committeeById(committeeId),
    start, end);
  
  return true;
}

bool showHelp()
{
  std::cout <<
    "Available commands:" << std::endl <<
    "--- list committees" << std::endl <<
    "--- add committee member" << std::endl <<
    "--- list members" << std::endl <<
    "--- add committee" << std::endl <<
    "--- list absence" << std::endl <<
    "--- add session" << std::endl <<
    "--- list committee counts" << std::endl <<
    "--- help" << std::endl;

  return true;
}

bool addSession()
{
  BusinessLogicController controller(ObjectModel::instance());

  std::cout << "Committees:" << std::endl;
  BOOST_FOREACH(const CommitteeManager::CommitteePair &p, ObjectModel::instance().committees().committees())
    std::cout << "ID:" << p.second.id() << " Aim: " << p.second.aim() << std::endl;

  std::cout << "Enter committee id: ";
  const std::string committeeId = getInputLine(std::cin);

  Committee committee = *ObjectModel::instance().committees().committeeById(committeeId);

  std::cout << "Member List: " << std::endl;
  std::vector<CouncilMember> currentMembers = controller.currentCommitteeMembers(committee);
  BOOST_FOREACH (const CouncilMember& member, currentMembers)
    std::cout << "ID: "<< member.id() << " Name: " << member.name() << std::endl;

  std::cout << "Enter members in row: ";
  const std::string memberIds = getInputLine(std::cin);

  std::vector<CouncilMember> members;
  std::istringstream stream;
  stream.str(memberIds);
  while (!stream.eof())
  {
    std::string memberId;
    stream >> memberId;
    if (!memberId.empty())
    {
      const CouncilMember *member = ObjectModel::instance().councilMembers().memberById(memberId);
      if (member)
        members.push_back(*member);
    }
  }

  std::cout << "Enter session date (YYYY MM DD): ";
  const std::string sessionDateString = getInputLine(std::cin);
  time_t sessionDate = dateFromFormattedString(sessionDateString);

  controller.addSession(sessionDate, committee, members);
  return true;
} 

bool listCommitteesWithCount()
{
  std::cout << "Enter start date (YYYY MM DD): ";
  const std::string startDate = getInputLine(std::cin);
  time_t start = dateFromFormattedString(startDate);

  std::cout << "Enter end date (YYYY MM DD): ";
  const std::string endDate = getInputLine(std::cin);
  time_t end = dateFromFormattedString(endDate);

  BusinessLogicController controller(ObjectModel::instance());
  BOOST_FOREACH(const CommitteeManager::CommitteePair &p, ObjectModel::instance().committees().committees())
  {
    std::cout << p.second.aim() << " with " <<
      controller.getSessionCount(p.second, start, end) << " sessions" << std::endl;
  }
  return true;
}

bool processCommand(const std::string& command)
{
  if (command == "list committees")
    return listCommittees();
  else if (command == "add committee member")
    return addCommitteeMember();
  else if (command == "list members")
    return listMembers();
  else if (command == "add committee")
    return addCommittee();
  else if (command == "list absence")
    return showAbsenceList();
  else if (command == "add session")
    return addSession();
  else if (command == "list committee counts")
    return listCommitteesWithCount();
  else if (command == "help")
    return showHelp();
  else
    return false;
}